package lj.a.protocol.lj.bean;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.StaticLog;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import ntd.tool.ToolId;

/**
 * LJ 自定义tcp报文协议
 * <p>
 * LJ{32:msgCode}{1:isReply}{1:isFile}{1:isFinal}{1:isStart}{bytes}LJ
 *
 * @author: liuwenju
 * @date: 2023/6/20 9:50
 * @version: 1.0
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class LJProtocolInfo {

    public static final byte[] BYTES_TAG = "LJLJLJLJLJ".getBytes();
    /**
     * 消息编码 / 文件 - 临时文件标识 - 32位固定长度
     */
    private String msgCode = ToolId.genId();

    /**
     * 目标设备
     */
    private String goalTerminal = "";

    /**
     * 数据区
     */
    private byte[] data = new byte[]{};

    /**
     * 是否回复消息
     */
    private boolean isReply = false;

    /**
     * 消息体
     */
    private byte[] bytes = new byte[]{};

    public byte[] toBytes() {
        byte[] goalBytes = StrUtil.blankToDefault(goalTerminal, "").getBytes();

        byte[] dataBytes = ArrayUtil.addAll(
                msgCode.getBytes(),
                boolToBytes(isReply),
                ByteUtil.intToBytes(goalBytes.length),
                goalBytes,
                data
        );

        return ArrayUtil.addAll(
            BYTES_TAG,
            ByteUtil.intToBytes(dataBytes.length),
            dataBytes,
            bytes,
            BYTES_TAG
        );
    }

    public static LJProtocolInfo parse(byte[] bytes) {
        int index = 0;
        // 目标设备名称长度
        int goalLength = 0;
        // 数据包长度
        int length = 0;
        // 清理前后缀
        bytes = removeTag(bytes);

        LJProtocolInfo ljProtocolInfo = new LJProtocolInfo();

        length = ByteUtil.bytesToInt(ArrayUtil.sub(bytes, index, (index = index + 4)));

        byte[] dataBytes = ArrayUtil.sub(bytes, 0, index + length);

        ljProtocolInfo.setBytes(ArrayUtil.sub(bytes, index + length, bytes.length));

        ljProtocolInfo.setMsgCode(new String(ArrayUtil.sub(dataBytes, index, (index = index + 32))));
        ljProtocolInfo.setReply(byteToBool(dataBytes[index++]));
        goalLength = ByteUtil.bytesToInt(ArrayUtil.sub(dataBytes, index, (index = index + 4)));
        ljProtocolInfo.setGoalTerminal(new String(ArrayUtil.sub(dataBytes, index, (index = index + goalLength))));
        ljProtocolInfo.setData(ArrayUtil.sub(dataBytes, index, dataBytes.length));
        return ljProtocolInfo;
    }

    public static void main(String[] args) {
        LJProtocolInfo ljProtocolInfo = new LJProtocolInfo();
        ljProtocolInfo.setData("123456".getBytes());
        ljProtocolInfo.setBytes(FileUtil.readBytes("C:\\Users\\17391\\\\Desktop\\新建 文本文档.txt"));
        ljProtocolInfo.setGoalTerminal("xxxxxx");
        StaticLog.info(JSONUtil.toJsonStr(ljProtocolInfo));
        StaticLog.info(JSONUtil.toJsonStr(LJProtocolInfo.parse(ljProtocolInfo.toBytes())));
    }

    public static byte[] removeStartTag(byte[] bytes) {
        byte[] startBytes = ArrayUtil.sub(bytes, 0, BYTES_TAG.length);
        int startI = ArrayUtil.indexOfSub(ArrayUtil.wrap(startBytes), ArrayUtil.wrap(BYTES_TAG));
        if (startI < 0) {
            return bytes;
        }
        if (startI == 0) {
            return removeStartTag(ArrayUtil.sub(bytes, BYTES_TAG.length, bytes.length));
        }
        return bytes;
    }

    public static byte[] removeEndTag(byte[] bytes) {
        byte[] endBytes = ArrayUtil.sub(bytes, bytes.length - BYTES_TAG.length, bytes.length);
        int endI = ArrayUtil.indexOfSub(ArrayUtil.wrap(endBytes), ArrayUtil.wrap(BYTES_TAG));
        if (endI < 0) {
            return bytes;
        }
        if (endI == 0) {
            return removeEndTag(ArrayUtil.sub(bytes, 0, bytes.length - BYTES_TAG.length));
        }
        return bytes;
    }

    public static byte[] removeTag(byte[] bytes) {
        return removeEndTag(removeStartTag(bytes));
    }

    private static byte[] boolToBytes(boolean bool) {
        if (bool) {
            return new byte[]{1};
        } else {
            return new byte[]{0};
        }
    }

    private static boolean byteToBool(byte by) {
        return by == 1;
    }

}
